package stardust.product.utils.transmit;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URL;
import java.net.UnknownHostException;
import stardust.core.io.IOUtils;
import stardust.core.log.Log;
import stardust.core.network.NetworkSessionInputStream;
import stardust.core.network.TCPNetworkSession;
import stardust.core.network.protocol.http.HttpMethod;
import stardust.core.network.protocol.http.HttpRequestMeta;
import stardust.product.utils.transmit.auto.HTTPAuthentication;

public class HttpTransmitRelayConnector
{
  
  private Socket sourceSocket;
  private Socket targetSocket;
  
  private String remoteHost;
  private int remotePort;
  
  private TransmitRelaySender sender;
  private TransmitRelayReceiver receiver;
  
  private boolean printFlag;
  
  public HttpTransmitRelayConnector(Socket sourceSocket, String remoteHost, int remotePort)
  {
    this.sourceSocket = sourceSocket;
    this.remoteHost = remoteHost;
    this.remotePort = remotePort;
  }
  
  public void connect() throws UnknownHostException, IOException
  {
    this.sourceSocket.setSoTimeout(TransmitListener.TIMEOUT);
    this.sender = new TransmitRelaySender();
    this.sender.start();
  }
  
  public void disconnect()
  {
    try
    {
      this.sourceSocket.close();
    }
    catch (IOException e)
    {
      e.printStackTrace();
    }
    try
    {
      if (this.targetSocket != null)
      {
        this.targetSocket.close();
      }
    }
    catch (IOException e)
    {
      e.printStackTrace();
    }
    
    if (this.sender != null)
    {
      this.sender.interrupt();
    }
    
    if (this.receiver != null)
    {
      this.receiver.interrupt();
    }
  }
  
  private class TransmitRelaySender extends Thread
  {
    
    public TransmitRelaySender()
    {
      super.setName("TransmitRelaySender");
      super.setDaemon(false);
    }

    public void run()
    {
      try
      {
        HttpRequestMeta requestMeta = HttpRequestMeta.parse(new NetworkSessionInputStream(new TCPNetworkSession(HttpTransmitRelayConnector.this.sourceSocket)));
        
        if (requestMeta == null)
        {
          return;
        }
        
        String targetUrl = HttpTransmitRelayConnector.this.processRequest(requestMeta);
        URL u = new URL(targetUrl);
        
        /*
         * 验证客户端身份。
         */
        if (!HTTPAuthentication.doAuthenticate(requestMeta, HttpTransmitRelayConnector.this.sourceSocket))
        {
          return;
        }
        
        /*
         * 建立代理连接并启动数据转发线程。
         */
        HttpTransmitRelayConnector.this.remoteHost = u.getHost();
        HttpTransmitRelayConnector.this.remotePort = u.getPort() == -1 ? 80 : u.getPort();
        
        Log.global.info("socket[", HttpTransmitRelayConnector.this.sourceSocket.getInetAddress().getHostAddress(), ":", HttpTransmitRelayConnector.this.sourceSocket.getPort(), "] request connect to [" + HttpTransmitRelayConnector.this.remoteHost + ":" + HttpTransmitRelayConnector.this.remotePort + "] using [" + requestMeta.getMethod() + "]");
        
        HttpTransmitRelayConnector.this.targetSocket = new Socket(HttpTransmitRelayConnector.this.remoteHost, HttpTransmitRelayConnector.this.remotePort);
        HttpTransmitRelayConnector.this.targetSocket.setSoTimeout(TransmitListener.TIMEOUT);
        
        HttpTransmitRelayConnector.this.receiver = new TransmitRelayReceiver();
        HttpTransmitRelayConnector.this.receiver.start();
        
        if (requestMeta.getMethod() == HttpMethod.connect)
        {
          byte[] message =
            ("HTTP/1.1 200 OK\r\n"
             + "\r\n").getBytes();
          HttpTransmitRelayConnector.this.sourceSocket.getOutputStream().write(message);
          HttpTransmitRelayConnector.this.sourceSocket.getOutputStream().flush();
          
          // 转发数据。
          IOUtils.readToWrite(HttpTransmitRelayConnector.this.targetSocket.getInputStream(), HttpTransmitRelayConnector.this.sourceSocket.getOutputStream());
        }
        else
        {
          byte[] message =
            ("HTTP/1.1 501 Not Implemented\r\n"
             + "\r\n"
             + "HTTP Status 501(Only CONNECT Support)").getBytes();
          HttpTransmitRelayConnector.this.sourceSocket.getOutputStream().write(message);
          HttpTransmitRelayConnector.this.sourceSocket.getOutputStream().flush();
        }
      }
      catch (IOException e)
      {
//        e.printStackTrace();
      }
      finally
      {
        if (!HttpTransmitRelayConnector.this.printFlag)
        {
          HttpTransmitRelayConnector.this.printFlag = true;
          Log.global.info("disconnect socket[", HttpTransmitRelayConnector.this.sourceSocket.getInetAddress().getHostAddress(), ":", HttpTransmitRelayConnector.this.sourceSocket.getPort(), "]");
        }
        
        HttpTransmitRelayConnector.this.disconnect();
      }
    }
    
  }
  
  private class TransmitRelayReceiver extends Thread
  {
    
    public TransmitRelayReceiver()
    {
      super.setName("TransmitRelayReceiver");
      super.setDaemon(false);
    }

    public void run()
    {
      try
      {
        IOUtils.readToWrite(HttpTransmitRelayConnector.this.sourceSocket.getInputStream(), HttpTransmitRelayConnector.this.targetSocket.getOutputStream());
      }
      catch (IOException e)
      {
//        e.printStackTrace();
        
        if (!HttpTransmitRelayConnector.this.printFlag)
        {
          HttpTransmitRelayConnector.this.printFlag = true;
          Log.global.info("disconnect socket[", HttpTransmitRelayConnector.this.sourceSocket.getInetAddress().getHostAddress(), ":", HttpTransmitRelayConnector.this.sourceSocket.getPort(), "]");
        }
      }
      finally
      {
//        System.out.println("recv exit...");
        HttpTransmitRelayConnector.this.disconnect();
      }
    }
    
  }
  
  private String processRequest(HttpRequestMeta requestMeta) throws MalformedURLException
  {
    String strUrl;
    URL url;

    if (requestMeta.getMethod() == HttpMethod.connect)
    {
      // requestMeta.setRequestURI("http://" + requestMeta.getRequestURI());
    }

    if (!requestMeta.getRequestURI().toLowerCase().startsWith("http://"))
    {
      url = new URL("http://" + requestMeta.getRequestURI());
      strUrl = "http://" + requestMeta.getRequestURI();
    }
    else
    {
      url = new URL(requestMeta.getRequestURI());
      strUrl = requestMeta.getRequestURI();
    }

    if ("*/*".equals(requestMeta.getOthers().get("Accept")))
    {
      requestMeta.getOthers().put(
      "Accept",
      "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
    }

    if (requestMeta.getOthers().containsKey("Host"))
    {
      String[] host = requestMeta.getOthers().get("Host").split(":", 2);

      url = new URL("http://" + host[0] + ":" + (host.length > 1 ? host[1] : 80) + url.getPath());
    }
    else
    {
      requestMeta.getOthers().put("Host", url.getHost() + ":" + url.getPort());
    }

    return strUrl;
  }
  
}
